<ui:component xmlns:h="http://java.sun.com/jsf/html" xmlns:f="http://java.sun.com/jsf/core"
    xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:tr="http://myfaces.apache.org/trinidad"
    xmlns:trh="http://myfaces.apache.org/trinidad/html" xmlns:c="http://java.sun.com/jstl/core"
    xmlns:ajsfc="http://www.andromda.org/cartridges/jsf/facelets">
    <c:set var="jsIdPrefix" value="" />
    <c:if test="#{not empty parentId}">
        <c:set var="jsIdPrefix" value="#{parentId}:" />
    </c:if>
    <c:set var="autocompleteSubformId" value="_#{id}_subformAutocomplete" />
    <c:set var="hiddenId" value="hidden" />
    <c:set var="editId" value="edit" />
    <c:set var="jsEditId" value="#{jsIdPrefix}#{autocompleteSubformId}:#{editId}" />
    <c:set var="jsHiddenId" value="#{jsIdPrefix}#{autocompleteSubformId}:#{hiddenId}" />
    <c:set var="jsActionId" value="#{jsIdPrefix}#{autocompleteSubformId}:action" />
    <c:set var="jsTableId" value="#{jsIdPrefix}#{autocompleteSubformId}:table" />
    <c:set var="jsVisibleListId" value="#{jsIdPrefix}#{autocompleteSubformId}:visibleList" />
    <c:set var="jsHiddenListId" value="#{jsIdPrefix}#{id}" />
    <c:set var="timeoutName" value="_#{parentId}_#{id}_timeout_" />
    <c:set var="lineArrayName" value="_#{parentId}_#{id}_lineArray_" />
    <c:set var="selectedLineName" value="_#{parentId}_#{id}_selectedLine_" />
    <c:set var="orgValue" value="_#{parentId}_#{id}_orgValue_" />
    <c:set var="orgEditValue" value="_#{parentId}_#{id}_orgEditValue_" />
    <c:set var="hiddenEditValue" value="_#{parentId}_#{id}_hiddenEditValue"/>
    <c:set var="showResultTable" value="_#{parentId}_#{id}_showResultTable" />
    <c:set var="setValuesFromTable" value="_#{parentId}_#{id}_setValuesFromTable" />
    <c:set var="hideResultTable" value="_#{parentId}_#{id}_hideResultTable" />
    <c:set var="editKeydown" value="_#{parentId}_#{id}_editKeydown" />
    <c:set var="pprMonitor" value="_#{parentId}_#{id}_pprMonitor" />
    <c:set var="addPPRListener" value="_#{parentId}_#{id}_addPPRListener" />
    <c:set var="checkEmptyEdit" value="_#{parentId}_#{id}_checkEmptyEdit" />
    <c:set var="checkChangeEditFunc" value="_#{parentId}_#{id}_checkChangeEditFunc"/>
    <c:set var="addActionClick" value="_#{parentId}_#{id}_addActionClick" />
    <c:set var="removeActionClick" value="_#{parentId}_#{id}_removeActionClick" />
    <c:set var="addValueToLists" value="_#{parentId}_#{id}_addValueToLists" />
    <c:set var="getLeftTop" value="_#{parentId}_#{id}_getLeftTop"/>

    <tr:selectManyListbox inlineStyle="display:none" id="#{id}" value="#{value}" required="#{required}" simple="true" valuePassThru="true">
        <c:forEach var="item" items="#{value}">
            <tr:selectItem value="#{item}" />
        </c:forEach>
    </tr:selectManyListbox>
    <tr:subform id="#{autocompleteSubformId}">
        <trh:script type="text/javascript">
            var #{timeoutName}=null;
            var #{lineArrayName}=null;
            var #{selectedLineName}=null;
            var #{orgValue}='';
            var #{orgEditValue}='';
            var #{orgEditValue}='';
            var #{hiddenEditValue}='';
            function #{showResultTable}(ignoreEdit){
                window.clearTimeout(#{timeoutName});
                var edit=document.getElementById('#{jsEditId}');
                if(ignoreEdit || edit.value != ''){
                    #{selectedLineName} = null;
                    #{lineArrayName} = null;

                    var action=document.getElementById('#{jsActionId}')
                    action.click();
                } else {
                    #{hideResultTable}();
                } 
            }
            function #{getLeftTop}(_tag){
                var _tagTmp = _tag;
                var pt = [0,0];
                var loop = true
                do {
                    pt[0] += _tagTmp.offsetLeft;
                    pt[1] += _tagTmp.offsetTop;
                    if (_tagTmp.offsetParent == null) {
                        loop = false
                    } else if (_tagTmp.offsetParent.id == "pageContentInt") {
                        loop = false;
                    } else if (_tagTmp.offsetParent == "BODY") {
                        loop = false;
                    }
                    _tagTmp = _tagTmp.offsetParent;
                } while(loop == true);
                return pt;
            }
            function #{pprMonitor}(state)
            {
                var busy = state == TrRequestQueue.STATE_BUSY;
                if(busy){
                    document.getElementById('#{jsActionId}').className = 'autocomplete-button-action';
                } else {
                    document.getElementById('#{jsActionId}').className = 'autocomplete-button';
                    TrPage.getInstance().getRequestQueue().removeStateChangeListener(#{pprMonitor});
                    
                    var firstElement = document.getElementById('#{jsTableId}0');
                    if(firstElement){
                        var table = document.getElementById('#{jsTableId}');
                        var edit=document.getElementById('#{jsEditId}');
                    
                        #{lineArrayName} = table.getElementsByTagName('TD');
                        #{selectedLineName} = 0;
                        firstElement.className='autocomplete-selected-line';

                        var editOffset = #{getLeftTop}(edit);
                        table.style.display="";
                        table.style.left = editOffset[0];
                        table.style.top = editOffset[1]+edit.offsetHeight;
                        table.style.width = edit.offsetWidth;
                    
                        edit.focus();
                    } else {
                        #{selectedLineName} = null;
                        #{lineArrayName} = null;
                    }
                }
            }
            function #{addPPRListener}()
            {
                var requestQueue = TrPage.getInstance().getRequestQueue();
                requestQueue.addStateChangeListener(#{pprMonitor});
            }            
            function #{setValuesFromTable}(hiddenValue, editValue){
                #{orgValue} = document.getElementById('#{jsHiddenId}').value;
                #{orgEditValue} = #{hiddenEditValue};
                #{hiddenEditValue} = editValue;
                document.getElementById('#{jsEditId}').value=editValue;             //the order matters
                document.getElementById('#{jsHiddenId}').value=hiddenValue; 
                #{hideResultTable}();
            }
            function #{hideResultTable}(){
                window.clearTimeout(#{timeoutName});
                #{timeoutName}=null;
                document.getElementById('#{jsTableId}').style.display='none';
                #{selectedLineName}=null;
                #{lineArrayName}=null;
            }
            function #{checkEmptyEdit}(){
                var edit=document.getElementById('#{jsEditId}');
                var hidden=document.getElementById('#{jsHiddenId}');
                if(hidden.value == ''){
                    edit.value = '';
                } else {
                    edit.value = edit.value.replace(/^\s\s*/, '').replace(/\s\s*$/, ''); //trim 
                    if(edit.value == ''){
                        hidden.value = '';
                    }
                }
            }
            function #{editKeydown}(_event) {
                window.clearTimeout(#{timeoutName});
                var event = window.event || _event;
            
                var _keyCode = event.keyCode ? event.keyCode : event.which ? event.which : event.charCode;

                if(#{selectedLineName} == null){
                    if(_keyCode==40) { //down arrow
                        #{showResultTable}(true);
                    } else if(_keyCode==27){ //esc
                        #{setValuesFromTableFunc}(#{orgValue}, #{orgEditValue});                        
                        return false;
                    } else {
                        #{timeoutName}=window.setTimeout('#{showResultTable}(false);',900);
                    }
                } else {
                    if(_keyCode==38){//up arrow
                        if(#{selectedLineName} &gt; 0){
                            document.getElementById('#{jsTableId}'+#{selectedLineName}).className='autocomplete-line';
                            --#{selectedLineName};
                            document.getElementById('#{jsTableId}'+#{selectedLineName}).className='autocomplete-selected-line';
                        }
                    }else if(_keyCode==40){//down arrow
                        if((#{lineArrayName}.length - 1) &gt; #{selectedLineName}){
                            if(#{selectedLineName} &gt; -1)
                                document.getElementById('#{jsTableId}'+#{selectedLineName}).className='autocomplete-line';
                            ++#{selectedLineName};
                            document.getElementById('#{jsTableId}'+#{selectedLineName}).className='autocomplete-selected-line';
                        }
                    }else if(_keyCode==27){ //esc
                        #{setValuesFromTable}(#{orgValue}, #{orgEditValue});                        
                        return false;
                    }else if(_keyCode==13){ //enter
                        document.getElementById('#{jsTableId}'+#{selectedLineName}).onclick();
                        return false;
                    }else if(_keyCode==9){ //tab
                        document.getElementById('#{jsTableId}'+#{selectedLineName}).onclick();
                    } else {
                        #{timeoutName}=window.setTimeout('#{showResultTable}(false);',900);
                    }
                }
                return true;
            }
            function #{addValueToLists}(value, label){
                var hiddenList = document.getElementById('#{jsHiddenListId}');
                hiddenList[hiddenList.options.length]=new Option(label, value, true,true);
                var visibleList = document.getElementById('#{jsVisibleListId}');
                visibleList[visibleList.options.length]=new Option(label, value, false,false);
            }
            function #{addActionClick}(){
                var hiddenValue = document.getElementById('#{jsHiddenId}').value;
                if(hiddenValue != ''){
                    #{addValueToLists}(hiddenValue, document.getElementById('#{jsEditId}').value);
                    #{setValuesFromTable}('', '')
                } 
            }
            function #{removeActionClick} (){
                var hiddenList = document.getElementById('#{jsHiddenListId}');
                var visibleList = document.getElementById('#{jsVisibleListId}');
                for (var i = visibleList.length - 1; i&gt;=0; i--) {
                    if (visibleList.options[i].selected) {
                        visibleList.remove(i);
                        hiddenList.remove(i);
                    }
                }
            }
            function #{checkChangeEditFunc}(){
                var edit=document.getElementById('#{jsEditId}');
                var hidden=document.getElementById('#{jsHiddenId}');
                if(edit.value == ''){
                    hidden.value = '';
                }else if((#{selectedLineName}==null)&amp;&amp;(#{hiddenEditValue}!=edit.value)){
                    edit.value = #{hiddenEditValue};
                    hidden.value = #{orgValue};
                }
            }
        </trh:script>
        <h:inputHidden id="#{hiddenId}" />
        <h:panelGrid columns="2" border="0" cellpadding="0" cellspacing="0" >
            <h:panelGrid columns="1"  styleClass="autocomplete-panelGrid"  border="0" cellpadding="0" cellspacing="0">
                <h:inputText id="#{editId}" autocomplete="off" readOnly="#{readOnly}"
                    ondblclick="document.getElementById('#{jsActionId}').click(); return false;"
                    onkeydown="return #{editKeydown}(event);"
                    onchange="#{checkChangeEditFunc}(); return true;"
                    onblur="window.clearTimeout(#{timeoutName}); #{checkEmptyEdit}(); #{timeoutName}=window.setTimeout('#{hideResultTable}();',100);" />
                <f:facet name="footer">
                    <tr:selectManyListbox id="visibleList" simple="true" contentStyle="width:100%;border-top-width:0px;" valuePassThru="true">                
                        <c:forEach var="item" items="#{value}">
                            <f:selectItem itemValue="#{item}" itemLabel="#{ajsfc.valueFromConverter(item,itemConverterId)}" />
                        </c:forEach>
                    </tr:selectManyListbox>
                </f:facet>
            </h:panelGrid>
            <h:panelGrid columns="1"  styleClass="autocomplete-panelGrid"  >
                <tr:commandButton blocking="true" id="action" styleClass="autocomplete-button"
                    actionListener="#{locateBackingBean[locateActionListenerName]}" partialSubmit="true" text=""
                    immediate="true" onclick="#{addPPRListener}(); return true;">
                    <f:param name="searchFieldRequestParamName" value="#{jsEditId}" />
                </tr:commandButton>
                <tr:commandButton blocking="true" id="addAction" styleClass="autocomplete-button-multiple-add" onclick="#{addActionClick}(); return false;" />
                <tr:commandButton blocking="true" id="removeAction" styleClass="autocomplete-button-multiple-remove" onclick="#{removeActionClick}(); return false;" />
            </h:panelGrid>
            <tr:table inlineStyle="position:absolute; z-index:100; #{empty autocompleteResult ? 'display:none': ''}"
                id="table" var="row" value="#{autocompleteResult}" rows="20" width="100%" partialTriggers="::action"
                varStatus="status">
                <tr:column>
                    <div id="#{jsTableId}#{status.index}" class="autocomplete-line"
                        onmouseover="this.className='autocomplete-selected-line';"
                        onmouseout="this.className = 'autocomplete-line';"
                        onclick="#{setValuesFromTable}('#{row[locateValueFieldName]}','#{row[locateVisibleFieldName]}');">
                    <h:outputText value="#{row[locateVisibleFieldName]}" /></div>
                </tr:column>
            </tr:table>
            
        </h:panelGrid>
    </tr:subform>
</ui:component>